home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr05
/
xnot12a.zip
/
DISPLAY.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-06-16
|
31KB
|
1,181 lines
#include "jam.h"
/*
* The functions in this file handle redisplay. The
* redisplay system knows almost nothing about the editing
* process; the editing functions do, however, set some
* hints to eliminate a lot of the grinding. There is more
* that can be done; the "vtputc" interface is a real
* pig. Two conditional compilation flags; the GOSLING
* flag enables dynamic programming redisplay, using the
* algorithm published by Jim Gosling in SIGOA.
*
* NOTE that GOSLING is best used when emacs is running on some
* BIG cpu and sending characters over a wire to a tty device.
* GOSLING is very compute intensive! Note also that for MS Windows
* versions (cpu and display in one box) it is FAR better to turn
* GOSLING off and use WINDOW_IO which assumes char string output
* is very cheap. On Windows 3.1, it is probably twice as fast
* as opposed to using GOSLING w/o WINDOW_IO defined. (JAM)
*
*/
#include "def.h"
#include "kbd.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#define LINECOLOR(lp)\
(showtouchedlines && (lp->l_flag & LFCHANGE) ? CHIGH : CTEXT)
/*
* You can change these back to the types
* implied by the name if you get tight for space. If you
* make both of them "int" you get better code on the VAX.
* They do nothing if this is not Gosling redisplay, except
* for change the size of a structure that isn't used.
* A bit of a cheat.
*/
#define XCHAR char
#define XSHORT short
/* These defines really belong in sysdef.h
*/
#ifndef XCHAR
# define XCHAR int
# define XSHORT int
#endif
#ifdef STANDOUT_GLITCH
extern int SG; /* number of standout glitches */
#endif
/*
* A video structure always holds
* an array of characters whose length is equal to
* the longest line possible. Only some of this is
* used if "ncol" isn't the same as "NCOL".
*/
typedef struct video_{
#ifdef GOSLING
short v_hash; /* Hash code, for compares. */
XSHORT v_cost; /* Cost of display. */
#endif
char v_flag; /* Flag word. */
char v_color; /* Color of the line. */
char v_text[NCOL]; /* The actual characters. */
} VIDEO;
#define VFCHG 0x0001 /* Changed. */
#define VFHBAD 0x0002 /* Hash and cost are bad. */
#define VFEXT 0x0004 /* extended line (beond ncol) */
/*
* SCORE structures hold the optimal
* trace trajectory, and the cost of redisplay, when
* the dynamic programming redisplay code is used.
* If no fancy redisplay, this isn't used. The trace index
* fields can be "char", and the score a "short", but
* this makes the code worse on the VAX.
*/
typedef struct {
XCHAR s_itrace; /* "i" index for track back. */
XCHAR s_jtrace; /* "j" index for trace back. */
XSHORT s_cost; /* Display cost. */
} SCORE;
/* Blah - these are all globals but originate here. I hate
* that but am not going to move them in the interest of
* preserving the code's history (JAM)
*/
int sgarbf = TRUE; /* TRUE if screen is garbage. */
int vtrow = 0; /* Virtual cursor row. */
int vtcol = 0; /* Virtual cursor column. */
int tthue = CNONE; /* Current color. */
int ttrow = HUGEN; /* Physical cursor row. */
int ttcol = HUGEN; /* Physical cursor column. */
int tttop = HUGEN; /* Top of scroll region. */
int ttbot = HUGEN; /* Bottom of scroll region. */
static int lbound = 0; /* leftmost bound of the current line
being displayed */
/* virtual and physical screen arrays
*/
#ifndef GOSLING
static VIDEO *vscreen; /* Edge vector, virtual. */
static VIDEO *pscreen; /* Edge vector, physical. */
#else
# define STATIC static
# ifdef MSW /* problems with 64k data seg limits */
# ifndef WIN32
# undef STATIC
# define STATIC
# endif
# endif
STATIC VIDEO *vscreen[NROW-1]; /* Edge vector, virtual. */
STATIC VIDEO *pscreen[NROW-1]; /* Edge vector, physical. */
STATIC VIDEO video[2*(NROW-1)];/* Actual screen data. */
#endif
static VIDEO *blanks; /* Blank line image. */
static VOID rn_(ucopy,(VIDEO *vvp, VIDEO *pvp));
static VOID rn_(uline,(int row, VIDEO *vvp, VIDEO *pvp));
static void rn_(modeline,(EWINDOW *wp, BOOL activebp));
static int rn_(vtputb, (char c, int n));
#ifdef GOSLING
static VOID rn_(hash,(VIDEO *vp));
static VOID rn_(setscores,(int offs, int size));
static VOID rn_(traceback,(int offs, int size, int i, int j));
/*
* This matrix is written as an array because
* we do funny things in the "setscores" routine, which
* is very compute intensive, to make the subscripts go away.
* It would be "SCORE score[NROW][NROW]" in old speak.
* Look at "setscores" to understand what is up.
*/
static SCORE score[NROW*NROW];
#endif
/*
* Initialize the data structures used
* by the display code. The edge vectors used
* to access the screens are set up. The operating
* system's terminal I/O channel is set up. Fill the
* "blanks" array with ASCII blanks. The rest is done
* at compile time. The original window is marked
* as needing full update, and the physical screen
* is marked as garbage, so all the right stuff happens
* on the first call to redisplay.
*/
VOID vtinit()
{
#ifdef GOSLING
register VIDEO *vp;
#endif
register int i;
ttopen();
ttinit();
#ifndef GOSLING
/* Array indirection different when not doing
* cost computation; arrays allocated vs static
* and max size of screen (NROWs) can now be larger
* since vscreen & pscreen don't point into single array
* (why was that done anyway?? and I can fix that outside
* the GOSLING thing if I want but didn't bother since
* not using GOSLING anyways) (JAM)
*/
vscreen = (VIDEO *)calloc(NROW-1, sizeof(VIDEO));
pscreen = (VIDEO *)calloc(NROW-1, sizeof(VIDEO));
#else
vp = &video[0];
for (i=0; i<NROW-1; ++i) {
vscreen[i] = vp;
++vp;
pscreen[i] = vp;
++vp;
}
#endif
blanks = (VIDEO *)calloc(1, sizeof(VIDEO));
blanks->v_color = CTEXT;
for (i=0; i<NCOL; ++i)
blanks->v_text[i] = ' ';
}
/*
* Tidy up the virtual display system
* in anticipation of a return back to the host
* operating system. Right now all we do is position
* the cursor to the last line, erase the line, and
* close the terminal channel.
*
* Free memory allocted for VIDEO's if not GOSLING.
*/
VOID vttidy() {
ttcolor(CTEXT);
ttnowindow(); /* No scroll window. */
ttmove(nrow-1, 0); /* Echo line. */
tteeol();
tttidy();
ttflush(FALSE);
ttclose();
#ifndef GOSLING
free(vscreen);
free(pscreen);
free(blanks);
#endif
}
/*
* Move the virtual cursor to an origin
* 0 spot on the virtual display screen. I could
* store the column as a character pointer to the spot
* on the line, which would make "vtputc" a little bit
* more efficient. No checking for errors.
*/
VOID vtmove(row, col)
int row, col;
{
vtrow = row;
vtcol = col;
}
/*
* Write a character to the virtual display,
* dealing with long lines and the display of unprintable
* things like control characters. Also expand tabs every TABSTOP
* columns. This code only puts printing characters into
* the virtual display image. Special care must be taken when
* expanding tabs. On a screen whose width is not a multiple
* of TABSTOP, it is possible for the virtual cursor to hit the
* right margin before the next tab stop is reached. This
* makes the tab code loop if you are not careful.
* Three guesses how we found this.
*/
VOID vtputc(c)
register int c;
{
register VIDEO *vp;
#ifndef GOSLING
vp = &vscreen[vtrow];
#else
vp = vscreen[vtrow];
#endif
if (vtcol >= ncol)
vp->v_text[ncol-1] = '$';
else if (c == '\t')
{
do {
vtputc(' ');
} while (vtcol<ncol && (vtcol&TABROUND)!=0);
}
else if (ISCTRL(c))
{
vtputc('^');
vtputc(CCHR(c));
}
else
vp->v_text[vtcol++] = (char)c;
}
/* Put a character to the virtual screen in an extended line. If we are
* not yet on left edge, don't print it yet. Check for overflow on
* the right margin.
*/
VOID vtpute(c)
int c;
{
register VIDEO *vp;
#ifndef GOSLING
vp = &vscreen[vtrow];
#else
vp = vscreen[vtrow];
#endif
if (vtcol >= ncol) vp->v_text[ncol - 1] = '$';
else if (c == '\t') {
do {
vtpute(' ');
}
while (((vtcol + lbound)&TABROUND) != 0 && vtcol < ncol);
} else if (ISCTRL(c) != FALSE) {
vtpute('^');
vtpute(CCHR(c));
} else {
if (vtcol >= 0)
vp->v_text[vtcol] = (char)c;
++vtcol;
}
}
/* Erase from the end of the
* software cursor to the end of the
* line on which the software cursor is
* located. The display routines will decide
* if a hardware erase to end of line command
* should be used to display this.
*/
VOID vteeol()
{
register VIDEO *vp;
#ifndef GOSLING
vp = &vscreen[vtrow];
#else
vp = vscreen[vtrow];
#endif
while (vtcol < ncol)
vp->v_text[vtcol++] = ' ';
}
/*
* Make sure that the display is
* right. This is a three part process. First,
* scan through all of the windows looking for dirty
* ones. Check the framing, and refresh the screen.
* Second, make sure that "currow" and "curcol" are
* correct for the current window. Third, make the
* virtual and physical screens the same.
*/
VOID update()
{
register LINE *lp;
register EWINDOW *wp;
register VIDEO *vp1;
VIDEO *vp2;
register int i;
register int j;
register int c;
register int k;
register int hflag;
register int currow;
register int curcol;
#ifdef GOSLING
register int offs;
register int size;
#endif
if (mytypeahead())
return;
if (sgarbf) /* must update everything */
upmodes(WFMODE|WFHARD);
hflag = FALSE; /* Not hard. */
wp = wheadp;
while (wp != NULL) {
if (wp->w_flag != 0) { /* Need update. */
if ((wp->w_flag&WFFORCE) == 0) {
lp = wp->w_linep;
for (i=0; i<wp->w_ntrows; ++i) {
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_linep)
break;
lp = lforw(lp);
}
}
i = wp->w_force; /* Reframe this one. */
if (i > 0) {
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows-1;
} else if (i < 0) {
i += wp->w_ntrows;
if (i < 0)
i = 0;
} else
i = wp->w_ntrows/2;
lp = wp->w_dotp;
while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_flag |= WFHARD; /* Force full. */
out:
lp = wp->w_linep; /* Try reduced update. */
i = wp->w_toprow;
if ((wp->w_flag&~WFMODE) == WFEDIT) {
while (lp != wp->w_dotp) {
++i;
lp = lforw(lp);
}
#ifndef GOSLING
vscreen[i].v_color = LINECOLOR(lp);
vscreen[i].v_flag |= (VFCHG|VFHBAD);
#else
vscreen[i]->v_color = LINECOLOR(lp);
vscreen[i]->v_flag |= (VFCHG|VFHBAD);
#endif
vtmove(i, 0);
for (j=0; j<llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
} else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
hflag = TRUE;
while (i < wp->w_toprow+wp->w_ntrows) {
#ifndef GOSLING
vscreen[i].v_color = LINECOLOR(lp);
vscreen[i].v_flag |= (VFCHG|VFHBAD);
#else
vscreen[i]->v_color = LINECOLOR(lp);
vscreen[i]->v_flag |= (VFCHG|VFHBAD);
#endif
vtmove(i, 0);
if (lp != wp->w_bufp->b_linep) {
for (j=0; j<llength(lp); ++j)
vtputc(lgetc(lp, j));
lp = lforw(lp);
}
vteeol();
++i;
}
}
if ((wp->w_flag&WFMODE) != 0)
modeline(wp, (wp == curwp ? TRUE : FALSE));
wp->w_flag = 0;
wp->w_force = 0;
}
wp = wp->w_wndp;
}
lp = curwp->w_linep; /* Cursor location. */
currow = curwp->w_toprow;
while (lp != curwp->w_dotp) {
++currow;
lp = lforw(lp);
}
curcol = 0;
i = 0;
while (i < curwp->w_doto) {
c = lgetc(lp, i++);
if (c == '\t')
curcol |= TABROUND;
else if (ISCTRL(c) != FALSE)
++curcol;
++curcol;
}
if (curcol >= ncol - 1) { /* extended line. */
/* flag we are extended and changed */
#ifndef GOSLING
vscreen[currow].v_flag |= VFEXT | VFCHG;
#else
vscreen[currow]->v_flag |= VFEXT | VFCHG;
#endif
updext(currow, curcol); /* and output extended line */
} else lbound = 0; /* not extended line */
/* make sure no lines need to be de-extended because
* the cursor is no longer on them
*/
wp = wheadp;
while (wp != NULL) {
lp = wp->w_linep;
i = wp->w_toprow;
while (i < wp->w_toprow + wp->w_ntrows) {
k = LINECOLOR(lp);
#ifndef GOSLING
if (k != vscreen[i].v_color)
{
if (showtouchedlines) /* force */
vscreen[i].v_flag |= (VFCHG|VFHBAD);
vscreen[i].v_color = k;
}
if (vscreen[i].v_flag & VFEXT)
#else
if (k != vscreen[i]->v_color)
{
if (showtouchedlines) /* force */
vscreen[i]->v_flag |= (VFCHG|VFHBAD);
vscreen[i]->v_color = k;
}
if (vscreen[i]->v_flag & VFEXT)
#endif
{
/* always flag extended lines as changed
*/
#ifndef GOSLING
vscreen[i].v_flag |= VFCHG;
#else
vscreen[i]->v_flag |= VFCHG;
#endif
if ((wp != curwp) || (lp != wp->w_dotp) ||
(curcol < ncol - 1)) {
vtmove(i, 0);
for (j = 0; j < llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
/* this line no longer is extended
*/
#ifndef GOSLING
vscreen[i].v_flag &= ~VFEXT;
#else
vscreen[i]->v_flag &= ~VFEXT;
#endif
}
}
lp = lforw(lp);
++i;
}
/* if whole screen garbaged, fix up mode lines
*/
if (sgarbf != FALSE)
#ifndef GOSLING
vscreen[i].v_flag |= VFCHG;
#else
vscreen[i]->v_flag |= VFCHG;
#endif
/* and onward to the next window
*/
wp = wp->w_wndp;
}
if (sgarbf != FALSE) { /* Screen is garbage. */
sgarbf = FALSE; /* Erase-page clears */
epresf = FALSE; /* the message area. */
tttop = HUGEN; /* Forget where you set */
ttbot = HUGEN; /* scroll region. */
tthue = CNONE; /* Color unknown. */
ttmove(0, 0);
tteeop();
for (i=0; i<nrow-1; ++i) {
#ifndef GOSLING
uline(i, &vscreen[i], blanks);
ucopy(&vscreen[i], &pscreen[i]);
#else
uline(i, vscreen[i], blanks);
ucopy(vscreen[i], pscreen[i]);
#endif
}
ttmove(currow, curcol - lbound);
ttflush(FALSE);
return;
}
#ifdef GOSLING
if (hflag != FALSE) { /* Hard update? */
for (i=0; i<nrow-1; ++i) { /* Compute hash data. */
hash(vscreen[i]);
hash(pscreen[i]);
}
offs = 0; /* Get top match. */
while (offs != nrow-1) {
vp1 = vscreen[offs];
vp2 = pscreen[offs];
if (vp1->v_color != vp2->v_color
|| vp1->v_hash != vp2->v_hash)
break;
uline(offs, vp1, vp2);
ucopy(vp1, vp2);
++offs;
}
if (offs == nrow-1) { /* Might get it all. */
ttmove(currow, curcol - lbound);
ttflush(FALSE);
return;
}
size = nrow-1; /* Get bottom match. */
while (size != offs) {
vp1 = vscreen[size-1];
vp2 = pscreen[size-1];
if (vp1->v_color != vp2->v_color
|| vp1->v_hash != vp2->v_hash)
break;
uline(size-1, vp1, vp2);
ucopy(vp1, vp2);
--size;
}
if ((size -= offs) == 0) /* Get screen size. */
panic("Illegal screen size in update");
setscores(offs, size); /* Do hard update. */
traceback(offs, size, size, size);
for (i=0; i<size; ++i)
ucopy(vscreen[offs+i], pscreen[offs+i]);
ttmove(currow, curcol - lbound);
ttflush(FALSE);
return;
}
#endif
for (i=0; i<nrow-1; ++i) { /* Easy update. */
#ifndef GOSLING
vp1 = &vscreen[i];
vp2 = &pscreen[i];
#else
vp1 = vscreen[i];
vp2 = pscreen[i];
#endif
if ((vp1->v_flag&VFCHG) != 0) {
uline(i, vp1, vp2);
ucopy(vp1, vp2);
}
}
ttmove(currow, curcol - lbound);
ttflush(FALSE);
}
/*
* Update a saved copy of a line,
* kept in a VIDEO structure. The "vvp" is
* the one in the "vscreen". The "pvp" is the one
* in the "pscreen". This is called to make the
* virtual and physical screens the same when
* display has done an update.
*/
static VOID ucopy(vvp, pvp)
register VIDEO *vvp;
register VIDEO *pvp;
{
vvp->v_flag &= ~VFCHG; /* Changes done. */
pvp->v_flag = vvp->v_flag; /* Update model. */
#ifdef GOSLING
pvp->v_hash = vvp->v_hash;
pvp->v_cost = vvp->v_cost;
#endif
pvp->v_color = vvp->v_color;
bcopy(vvp->v_text, pvp->v_text, ncol);
}
/* updext: update the extended line which the cursor is currently
* on at a column greater than the terminal width. The line
* will be scrolled right or left to let the user see where
* the cursor is
*/
VOID updext(currow, curcol)
int currow, curcol;
{
register LINE *lp; /* pointer to current line */
register int j; /* index into line */
/* calculate what column the left bound should be
* (force cursor into middle half of screen)
*/
lbound = curcol - (curcol % (ncol>>1)) - (ncol>>2);
/* scan through the line outputing characters to the virtual screen
* once we reach the left edge
*/
vtmove(currow, -lbound); /* start scanning offscreen */
lp = curwp->w_dotp; /* line to output */
for (j=0; j<llength(lp); ++j) /* until the end-of-line */
vtpute(lgetc(lp, j));
/* truncate the virtual line and put a '$' in column 1
*/
vteeol();
#ifndef GOSLING
vscreen[currow].v_text[0] = '$';
#else
vscreen[currow]->v_text[0] = '$';
#endif
}
/*
* Update a single line. This routine only
* uses basic functionality (no insert and delete character,
* but erase to end of line). The "vvp" points at the VIDEO
* structure for the line on the virtual screen, and the "pvp"
* is the same for the physical screen. Avoid erase to end of
* line when updating CMODE color lines, because of the way that
* reverse video works on most terminals.
*
* NOTE if defined, uses dump-whole-line since presumed fastest
*/
static VOID uline(row, vvp, pvp)
int row;
register VIDEO *vvp;
register VIDEO *pvp;
{
#ifdef CURSES
register char *vc, *pc;
register int i = 0;
ttcolor(vvp->v_color);
ttmove(row, i);
ttputline(&vvp->v_text[i]);
#else
# ifdef WINDOW_IO
register char *vc, *pc;
register int i = 0;
/* Find first diff char, then dump line from there to
* end. If not same color, must dump whole line vs
* only outputting from 'difference to end of line'.
* NOTE this code written on premise that dumping a
* whole line is faster than dumping even just 2 chars
* in that line if those 2 chars not contigious, ie one I/O per
* line is fastest possible output means. (JAM)
*/
if (vvp->v_color == pvp->v_color)
{
vc = &vvp->v_text[0];
pc = &pvp->v_text[0];
while (vc[i] && (pc[i] == vc[i]))
i++;
# ifdef MSW
/* Serious HACK - bug with truetype fonts (?) causes
* left-edge character droppings when drawing some chars
* on top of others, ie E on V leaves drops to left. This
* hack backs up one char; the previous char is the same
* in old and new line. This fixes the 'problem'. Sheesh (JAM)
*/
if (i > 0)
i--;
# endif
}
ttcolor(vvp->v_color);
ttmove(row, i);
ttputline(&vvp->v_text[i]);
# else /* WINDOW_IO */
register char *cp1;
register char *cp2;
register char *cp3;
char *cp4;
char *cp5;
register int nbflag;
if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */
ttmove(row, 0); /* full redraw. */
# ifdef STANDOUT_GLITCH
if (pvp->v_color != CTEXT && SG >= 0)
tteeol();
# endif
ttcolor(vvp->v_color);
# ifdef STANDOUT_GLITCH
cp1 = &vvp->v_text[SG > 0 ? SG : 0];
/* the odd code for SG==0 is to avoid putting the invisable
* glitch character on the next line.
* (Hazeltine executive 80 model 30)
*/
cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG!=0 ? SG : 1) : 0)];
# else
cp1 = &vvp->v_text[0];
cp2 = &vvp->v_text[ncol];
# endif
while (cp1 != cp2) {
ttputc(*cp1++);
++ttcol;
}
# ifndef MOVE_STANDOUT
ttcolor(CTEXT);
# endif
return;
}
cp1 = &vvp->v_text[0]; /* Compute left match. */
cp2 = &pvp->v_text[0];
while (cp1!=&vvp->v_text[ncol] && cp1[0]==cp2[0]) {
++cp1;
++cp2;
}
if (cp1 == &vvp->v_text[ncol]) /* All equal. */
return;
nbflag = FALSE;
cp3 = &vvp->v_text[ncol]; /* Compute right match. */
cp4 = &pvp->v_text[ncol];
while (cp3[-1] == cp4[-1]) {
--cp3;
--cp4;
if (cp3[0] != ' ') /* Note non-blanks in */
nbflag = TRUE; /* the right match. */
}
cp5 = cp3; /* Is erase good? */
if (nbflag==FALSE && vvp->v_color==CTEXT) {
while (cp5!=cp1 && cp5[-1]==' ')
--cp5;
/* Alcyon hack */
if ((int)(cp3-cp5) <= tceeol)
cp5 = cp3;
}
/* Alcyon hack */
ttmove(row, (int)(cp1-&vvp->v_text[0]));
# ifdef STANDOUT_GLITCH
if (vvp->v_color != CTEXT && SG > 0) {
if(cp1 < &vvp->v_text[SG]) cp1 = &vvp->v_text[SG];
if(cp5 > &vvp->v_text[ncol-SG]) cp5 = &vvp->v_text[ncol-SG];
} else if (SG < 0)
# endif
ttcolor(vvp->v_color);
while (cp1 != cp5) {
ttputc(*cp1++);
++ttcol;
}
if (cp5 != cp3) /* Do erase. */
tteeol();
# endif /* WINDOW_IO */
#endif /* CURSES */
}
/*
* Redisplay the mode line for
* the window pointed to by the "wp".
* This is the only routine that has any idea
* of how the modeline is formatted. You can
* change the modeline format by hacking at
* this routine. Called by "update" any time
* there is a dirty window.
* Note that if STANDOUT_GLITCH is defined, first and last SG characters
* may never be seen.
*/
static VOID modeline(wp, activebp)
register EWINDOW *wp;
BOOL activebp;
{
register int n;
register BUFFER *bp;
int mode;
int flag;
char fchar = (activebp ? '-' : ' ');
n = wp->w_toprow+wp->w_ntrows; /* Location. */
#ifndef GOSLING
vscreen[n].v_color = CMODE; /* Mode line color. */
vscreen[n].v_flag |= (VFCHG|VFHBAD); /* Recompute, display. */
#else
vscreen[n]->v_color = CMODE; /* Mode line color. */
vscreen[n]->v_flag |= (VFCHG|VFHBAD); /* Recompute, display. */
#endif
vtmove(n, 0); /* Seek to right line. */
/* reset n, now a column counter
*/
n = 0;
bp = wp->w_bufp;
flag = ((bp->b_flag & BFCHG) ? TRUE : FALSE); /* "*" if changed. */
vtputc(fchar); n++;
if (somemail)
{
n += vtputs(" Mail ");
vtputc(fchar); n++;
}
#ifndef WINDOWED
if (activebp)
n += vtputs(g_APPNAME);
else
{
n = strlen(g_APPNAME);
vtputb(' ', n);
}
vtputc(fchar); n++;
#endif
if ((bp->b_flag&BFVIEW) != 0) /* "r" if readonly. */
vtputc('%');
else if (flag)
vtputc('*');
else
vtputc(fchar);
n++;
if (flag && !(bp->b_flag & BFINC) && /* tells if incremental save */
bp->b_fname[0])
vtputc('+'); /* up-to-date */
else if (flag && bp->b_fname[0])
vtputc('!'); /* changes not flushed */
else
vtputc(fchar); /* no changes, ok */
n++;
vtputb(fchar, 2); n += 2;
if (bp->b_bname[0] != '\0')
n += vtputs(&(bp->b_bname[0]));
#ifndef SHOWFILENAME /* jam.h */
# define MODE_OFFSET 25
# else
# define MODE_OFFSET 62
if (bp->b_fname[0] != '\0')
{
int x = strlen(bp->b_fname);
vtputb(' ', 3);
n += 3;
if (x <= MAX_FNAME_CHARS)
n += vtputs(&(bp->b_fname[0]));
else
{
char buf[MIN_FNAME_CHARS];
bzero(buf, MIN_FNAME_CHARS);
vtputb('.', 2); n += 2;
x = MIN_FNAME_CHARS + (x - MAX_FNAME_CHARS);
n += vtputs(&bp->b_fname[x]);
}
vtputc(' '); n++;
#ifdef DOCRYPT
if (bp->b_flag & BFCRYPT)
{
static char *key = "(Key!)";
n += vtputs(key);
}
#endif /* CRYPT */
vtputc(' '); n++;
}
#endif
while (n < MODE_OFFSET)
{
vtputc(' ');
n++;
}
if (activebp)
{
vtputc('('); n++;
for (mode=0; ;)
{
n += vtputs(bp->b_modes[mode]->p_name);
if ((bp->b_modes[mode] == name_mode("C")) && isCindent())
{
vtputc('!');
n++;
}
if(++mode > bp->b_nmodes)
break;
vtputc('-'); n++;
}
vtputc(')'); n++;
}
while (n < ncol)
{ /* Pad out. */
if ((n == 79) && activebp) /* count from 0 */
vtputc('^'); /* 80 col marker */
else
vtputc(fchar);
n++;
}
}
/*
* output a string, report how long it was.
*/
vtputs(s)
register char *s;
{
register int n = 0;
while (*s != '\0')
{
vtputc(*s++);
++n;
}
return n;
}
#ifdef GOSLING
/*
* Compute the hash code for
* the line pointed to by the "vp". Recompute
* it if necessary. Also set the approximate redisplay
* cost. The validity of the hash code is marked by
* a flag bit. The cost understand the advantages
* of erase to end of line. Tuned for the VAX
* by Bob McNamara; better than it used to be on
* just about any machine.
*/
static VOID hash(vp)
register VIDEO *vp;
{
register int i;
register int n;
register char *s;
if ((vp->v_flag&VFHBAD) != 0) { /* Hash bad. */
s = &vp->v_text[ncol-1];
for (i=ncol; i!=0; --i, --s)
if (*s != ' ')
break;
n = ncol-i; /* Erase cheaper? */
if (n > tceeol)
n = tceeol;
vp->v_cost = i+n; /* Bytes + blanks. */
for (n=0; i!=0; --i, --s)
n = (n<<5) + n + *s;
vp->v_hash = n; /* Hash code. */
vp->v_flag &= ~VFHBAD; /* Flag as all done. */
}
}
/*
* Compute the Insert-Delete
* cost matrix. The dynamic programming algorithm
* described by James Gosling is used. This code assumes
* that the line above the echo line is the last line involved
* in the scroll region. This is easy to arrange on the VT100
* because of the scrolling region. The "offs" is the origin 0
* offset of the first row in the virtual/physical screen that
* is being updated; the "size" is the length of the chunk of
* screen being updated. For a full screen update, use offs=0
* and size=nrow-1.
*
* Older versions of this code implemented the score matrix by
* a two dimensional array of SCORE nodes. This put all kinds of
* multiply instructions in the code! This version is written to
* use a linear array and pointers, and contains no multiplication
* at all. The code has been carefully looked at on the VAX, with
* only marginal checking on other machines for efficiency. In
* fact, this has been tuned twice! Bob McNamara tuned it even
* more for the VAX, which is a big issue for him because of
* the 66 line X displays.
*
* On some machines, replacing the "for (i=1; i<=size; ++i)" with
* i = 1; do { } while (++i <=size)" will make the code quite a
* bit better; but it looks ugly.
*/
static VOID setscores(offs, size)
int offs, size;
{
register SCORE *sp;
SCORE *sp1;
register int tempcost;
register int bestcost;
register int j;
register int i;
register VIDEO **vp;
VIDEO **pp, **vbase, **pbase;
vbase = &vscreen[offs-1]; /* By hand CSE's. */
pbase = &pscreen[offs-1];
score[0].s_itrace = 0; /* [0, 0] */
score[0].s_jtrace = 0;
score[0].s_cost = 0;
sp = &score[1]; /* Row 0, inserts. */
tempcost = 0;
vp = &vbase[1];
for (j=1; j<=size; ++j) {
sp->s_itrace = 0;
sp->s_jtrace = (XCHAR)(j-1);
tempcost += tcinsl;
tempcost += (*vp)->v_cost;
sp->s_cost = tempcost;
++vp;
++sp;
}
sp = &score[NROW]; /* Column 0, deletes. */
tempcost = 0;
for (i=1; i<=size; ++i) {
sp->s_itrace = (XCHAR)(i-1);
sp->s_jtrace = 0;
tempcost += tcdell;
sp->s_cost = tempcost;
sp += NROW;
}
sp1 = &score[NROW+1]; /* [1, 1]. */
pp = &pbase[1];
for (i=1; i<=size; ++i) {
sp = sp1;
vp = &vbase[1];
for (j=1; j<=size; ++j) {
sp->s_itrace = (XCHAR)(i-1);
sp->s_jtrace = (XCHAR)j;
bestcost = (sp-NROW)->s_cost;
if (j != size) /* Cd(A[i])=0 @ Dis. */
bestcost += tcdell;
tempcost = (sp-1)->s_cost;
tempcost += (*vp)->v_cost;
if (i != size) /* Ci(B[j])=0 @ Dsj. */
tempcost += tcinsl;
if (tempcost < bestcost) {
sp->s_itrace = (XCHAR)i;
sp->s_jtrace = (XCHAR)(j-1);
bestcost = tempcost;
}
tempcost = (sp-NROW-1)->s_cost;
if ((*pp)->v_color != (*vp)->v_color
|| (*pp)->v_hash != (*vp)->v_hash)
tempcost += (*vp)->v_cost;
if (tempcost < bestcost) {
sp->s_itrace = (XCHAR)(i-1);
sp->s_jtrace = (XCHAR)(j-1);
bestcost = tempcost;
}
sp->s_cost = bestcost;
++sp; /* Next column. */
++vp;
}
++pp;
sp1 += NROW; /* Next row. */
}
}
/*
* Trace back through the dynamic programming cost
* matrix, and update the screen using an optimal sequence
* of redraws, insert lines, and delete lines. The "offs" is
* the origin 0 offset of the chunk of the screen we are about to
* update. The "i" and "j" are always started in the lower right
* corner of the matrix, and imply the size of the screen.
* A full screen traceback is called with offs=0 and i=j=nrow-1.
* There is some do-it-yourself double subscripting here,
* which is acceptable because this routine is much less compute
* intensive then the code that builds the score matrix!
*/
static VOID traceback(offs, size, i, j)
int offs, size, i, j;
{
register int itrace;
register int jtrace;
register int k;
register int ninsl;
register int ndraw;
register int ndell;
if (i==0 && j==0) /* End of update. */
return;
itrace = score[(NROW*i) + j].s_itrace;
jtrace = score[(NROW*i) + j].s_jtrace;
if (itrace == i) { /* [i, j-1] */
ninsl = 0; /* Collect inserts. */
if (i != size)
ninsl = 1;
ndraw = 1;
while (itrace!=0 || jtrace!=0)
{
if (score[(NROW*itrace) + jtrace].s_itrace != (XCHAR)itrace)
break;
jtrace = (int)score[(NROW*itrace) + jtrace].s_jtrace;
if (i != size)
++ninsl;
++ndraw;
}
traceback(offs, size, itrace, jtrace);
if (ninsl != 0) {
ttcolor(CTEXT);
ttinsl(offs+j-ninsl, offs+size-1, ninsl);
}
do { /* B[j], A[j] blank. */
k = offs+j-ndraw;
uline(k, vscreen[k], blanks);
} while (--ndraw);
return;
}
if (jtrace == j) { /* [i-1, j] */
ndell = 0; /* Collect deletes. */
if (j != size)
ndell = 1;
while (itrace!=0 || jtrace!=0)
{
if (score[(NROW*itrace) + jtrace].s_jtrace != (XCHAR)jtrace)
break;
itrace = (int)score[(NROW*itrace) + jtrace].s_itrace;
if (j != size)
++ndell;
}
if (ndell != 0) {
ttcolor(CTEXT);
ttdell(offs+i-ndell, offs+size-1, ndell);
}
traceback(offs, size, itrace, jtrace);
return;
}
traceback(offs, size, itrace, jtrace);
k = offs+j-1;
uline(k, vscreen[k], pscreen[offs+i-1]);
}
#endif /* GOSLING */
/* put N copies of char c
*/
static int vtputb(c, n)
char c;
int n;
{
register int i = n;
while (i)
{
vtputc(c);
i--;
}
return(n);
}